﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Xml;

namespace Gonte.Xml
{
    /// <summary>
    /// Traverses an xml text using an xml reader
    /// </summary>
    public class XmlTraversal
    {
        #region Methods

        public void Traverse()
        {
            if (reader_ == null)
            {
                throw new ArgumentNullException("Reader");
            }

            if (!OnBeforeReading())
            {
                return;
            }

            while (reader_.Read())
            {
                Debug.WriteLine(
                string.Format("*** Reading node with name: {0}, type: {1} and value: {2}", 
                reader_.Name, 
                reader_.NodeType.ToString(),
                string.IsNullOrEmpty(Reader.Value) ? "null" : Reader.Value)
                );

                switch (reader_.NodeType)
                {
                    case XmlNodeType.Text:
                        {
                            if (!OnText())
                            {
                                return;
                            }
                        }
                        break;
                    case XmlNodeType.CDATA:
                        {
                            if (!OnText())
                            {
                                return;
                            }
                        }
                        break;
                    case XmlNodeType.Element:
                        {
                            if (!ProcessElement())
                            {
                                return;
                            }
                        }
                        break;
                    case XmlNodeType.EndElement:
                        {
                            if (!OnEndElement())
                            {
                                return;
                            }
                        }
                        break;
                    //case XmlNodeType.Attribute: does not work
                }
            }
        }

        #endregion

        #region Properties

        public XmlReader Reader
        {
            get { return reader_; }
            set { reader_ = value; }
        }

        public int Level
        {
            get { return level_; }
            //set { level_ = value; }
        }

        #endregion

        #region Overridables

        protected virtual bool OnBeforeReading()
        {
            return true;
        }

        protected virtual bool ProcessElement()
        {
            if (OnElement())
            {
                if (reader_.HasAttributes) // Read all the attributes
                {
                    while (reader_.MoveToNextAttribute())
                    {
                        OnAttribute();
                    }

                    reader_.MoveToElement(); // Move the reader back to the element node.
                }
            }

            if (reader_.IsEmptyElement)
            {
                if (!OnEndElement())
                {
                    return false;
                }
            }

            return true;
        }

        protected virtual bool OnText()
        {
            Debug.WriteLine(string.Format("Text node with text: {0}", reader_.Value.Trim()));
            return true; // Continue reading
        }

        protected virtual bool OnElement()
        {
            IncrementLevel();
            return true; // Process the attributes
        }

        protected virtual bool OnEndElement()
        {
            DecrementLevel();
            return true; // Continue reading
        }

        protected virtual void OnAttribute()
        {
            Debug.WriteLine(string.Format("Attribute: {0}", reader_.Name));
        }

        #endregion

        protected void IncrementLevel()
        {
            ++level_;

            Debug.WriteLine(string.Format("Start element with name: {0} and level: {1}",
                reader_.LocalName,
                level_));
        }

        protected void DecrementLevel()
        {
            --level_;

            Debug.WriteLine(string.Format("End element with name: {0} and level {1}",
                reader_.LocalName,
                level_));
        }


        #region Fields

        private XmlReader reader_;
        private int level_ = 0;

        #endregion
    }
}
